package net.lookyou.boot.demo.config.security;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

import javax.annotation.Resource;
import javax.sql.DataSource;

/**
 * <p> Security 核心配置类 </p>
 *
 * @author： zhengqing <br/>
 * @date： 2019/9/30$ 10:58$ <br/>
 * @version： <br/>
 */
@Configuration
@EnableWebSecurity
@ConfigurationProperties("system")
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig {

    @Resource
    private DataSource dataSource;

    /** 免认证地址 **/
    private String[] uncheckArray;

    public String[] getUncheckArray() {
        return uncheckArray;
    }

    public void setUncheckArray(String[] uncheckArray) {
        this.uncheckArray = uncheckArray;
    }

    /** 用户密码校验过滤器 **/
    private final CustomerAuthenticationProcessingFilter customerAuthenticationProcessingFilter;

    /** 用户认证信息 **/
    @Resource
    private CustomerUserDetailsService customerUserDetailsService;

    /** 未登陆时访问需要认证的地址时返回 **/
    @Resource
    private CustomerAuthenticationEntryPoint customerAuthenticationEntryPoint;


    public SecurityConfig(CustomerAuthenticationProcessingFilter customerAuthenticationProcessingFilter) {
        this.customerAuthenticationProcessingFilter = customerAuthenticationProcessingFilter;
    }

    /** 权限配置 **/
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.antMatcher("/**").authorizeRequests();
        // 禁用CSRF 开启跨域
        http.csrf().disable().cors();
        //未登陆时访问需要认证的地址时返回
        http.exceptionHandling().authenticationEntryPoint(customerAuthenticationEntryPoint);
        // 自动登录 - cookie储存方式
        registry.and().rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(31536000).userDetailsService(customerUserDetailsService);
        //不校验的路径
        registry.antMatchers(HttpMethod.GET,uncheckArray).permitAll();
        // 标识只能在 服务器本地ip[127.0.0.1或localhost] 访问`/home`接口，其他ip地址无法访问
        registry.antMatchers("/home").hasIpAddress("127.0.0.1");
        // 允许匿名的url - 可理解为放行接口 - 多个接口使用,分割
        registry.antMatchers("/login", "/index").permitAll();
        // OPTIONS(选项)：查找适用于一个特定网址资源的通讯选择。 在不需执行具体的涉及数据传输的动作情况下， 允许客户端来确定与资源相关的选项以及 / 或者要求， 或是一个服务器的性能
        registry.antMatchers(HttpMethod.OPTIONS, "/**").denyAll();
        //登出服务
        registry.and().logout().logoutUrl("/logout").logoutSuccessUrl("/login.html").permitAll();
        // 其余所有请求都需要认证
        registry.anyRequest().authenticated();
        // 防止iframe 造成跨域
        registry.and().headers().frameOptions().disable();
        // 自定义过滤器认证用户名密码
        http.addFilterAt(customerAuthenticationProcessingFilter, UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }

    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl persistentTokenRepository = new JdbcTokenRepositoryImpl();
        persistentTokenRepository.setDataSource(dataSource);
        //persistentTokenRepository.setCreateTableOnStartup(true);
        return persistentTokenRepository;
    }
}

